package ar.com.spsolutions.generators;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import ar.com.spsolutions.splibrary.library.Library;

public class BuildGenerator {

    private Class<?> target;

    public BuildGenerator(final Class<?> target) {
        this.target = target;
    }

    public void build() {
        String packageName = target.getPackage().getName();
        Method[] setters = getAllSetters();
        Set<String> imports = getImports(setters);
        String className = target.getSimpleName() + "Builder";
        String withs = makeWiths(setters, className);
        String getter = makeGetter();
        String buildMethod = makeBuildMethod();
        System.out.println(build(packageName, imports, className, makeField(), makeContructor(className), withs,
                getter, buildMethod));
    }

    private String makeBuildMethod() {
        return "public " + target.getSimpleName() + " build(){\n" + target.getSimpleName() + " temp = getToBuild();\n"
                + "toBuild = new " + target.getSimpleName() + "();\n" + "return temp;\n}\n";

    }

    private String makeGetter() {
        return "private " + target.getSimpleName() + " getToBuild(){ return toBuild;}\n";
    }

    private String makeContructor(final String className) {
        StringBuffer builder = new StringBuffer();
        builder.append("public " + className + "(){\n");
        builder.append("toBuild = new " + target.getSimpleName() + "();");
        builder.append("\n}");
        return builder.toString();
    }

    private String makeWiths(final Method[] setters, final String className) {
        StringBuffer builder = new StringBuffer();
        for (Method method : setters) {
            builder.append("\npublic " + className + " " + method.getName().replaceFirst("set", "with") + "("
                    + method.getParameterTypes()[0].getSimpleName() + " value){\n");
            builder.append("getToBuild()." + method.getName() + "(value);\n");
            builder.append("return this;\n");
            builder.append("}\n");
        }

        return builder.toString();
    }

    private String makeField() {
        return "private transient " + target.getSimpleName() + " toBuild;";
    }

    private String build(final String packageName, final Set<String> imports, final String className,
            final String field, final String constructor, final String withs, final String getter,
            final String buildMethod) {
        StringBuffer builder = new StringBuffer();
        builder.append("package " + packageName + ";\n\n");
        for (String importClass : imports) {
            builder.append("import " + importClass + ";\n");
        }
        builder.append("\npublic class " + className + " {\n\n");
        builder.append(field + "\n\n");
        builder.append(constructor + "\n");
        builder.append(withs);
        builder.append(buildMethod);
        builder.append(getter);
        builder.append("}");
        return builder.toString();
    }

    private Set<String> getImports(final Method[] setters) {
        Set<String> toReturn = new HashSet<String>();
        for (Method method : setters) {
            Class<?> param = method.getParameterTypes()[0];
            toReturn.add(param.getCanonicalName());

        }
        return toReturn;
    }

    private Method[] getAllSetters() {
        Method[] methods = target.getDeclaredMethods();
        List<Method> toReturn = new ArrayList<Method>();
        for (Method method : methods) {
            if (isSetter(method.getName())) {
                toReturn.add(method);
            }
        }
        return toReturn.toArray(new Method[toReturn.size()]);
    }

    private boolean isSetter(final String name) {
        if (name.length() < 3) {
            return false;
        }
        if (!name.startsWith("set")) {
            return false;
        }
        String withoutSet = name.replaceFirst("set", "");
        String varName = withoutSet.substring(0, 1).toLowerCase() + withoutSet.substring(1);
        try {
            target.getDeclaredField(varName);
        } catch (SecurityException e) {
            throw new UnsupportedOperationException();
        } catch (NoSuchFieldException e) {
            return false;
        }
        return true;
    }

    public static void main(final String[] args) {
        BuildGenerator generator = new BuildGenerator(Library.class);
        generator.build();
    }
}
